package ngat.oss;

import ngat.message.OSS.*;
import ngat.astrometry.*;
import ngat.util.logging.*;
import ngat.phase2.*; 
import ngat.instrument.*;
import ngat.phase2.nonpersist.*;

import java.util.*;
import java.io.*;

import jyd.dbms.*;
import jyd.storable.*;
import jyd.collection.*;

/**
 * Action: Sent by USR to replace a Proposal specification after editing 
 * @author S.N.Fraser
 * $Id: REPLACE_PROPOSALImpl.java,v 1.1 2006/11/27 08:45:00 snf Exp $
 */
public class REPLACE_PROPOSALImpl extends TransactionImpl {
    
    /** The path used to locate the Proposal in the OSS database. */
    protected Path proposalPath;
    
    /** The replacement (updated) Proposal to replace the one at 'path'.*/
    protected Proposal replacementProposal;
    
    /** The key to unlock the Proposal record. */
    protected int key;
    
 
    /** Create a REPLACE_PROPOSALImpl for the supplied request.*/
    public REPLACE_PROPOSALImpl(REPLACE_PROPOSAL request) {
	super(request);
	this.proposalPath        = request.getProposalPath();
	this.replacementProposal = request.getReplacementProposal();
	this.key                 = request.getKey();
    }

    /**
     * Replaces the Proposal specified
     * @return Boolean or an OSS_Error.
     */
    public TRANSACTION_DONE exec() { 

	myLog = LogManager.getLogger("TRACE");

	Proposal proposal = null;
	
	User     user = null;
	
	Transaction t = null;
	try {
	    t = Transaction.current();
	    
	    myLog.log(1, "Replacing Proposal at: "+proposalPath+" with: "+
		      (replacementProposal != null ? replacementProposal.getFullPath() : "NULL"));
	    
	    // Locate the Proposal's owner (PI) from its path expression.
	    
	    user = OSS_Utilities.fetchUser(proposalPath);
	    myLog.log(1, "Fetching user info...");
	    // Check if it exists.
	    if (user == null) {
		myLog.log(1, CLASS, id, "exec", 
			  "User: "+proposalPath.getUserByName()+" (owner for Proposal: "
			  +proposalPath.toString()+") Not in database.");
		return error(TRANSACTION.PATH_RESOLUTION_ERROR,
			     "Could not find owner for Proposal: "
			     +proposalPath.getProposalByName());
	    }
	    myLog.log(1, "Found Proposal's owner, user: "+user.getName());

	    // Locate the Proposal record from its path expression.
	    proposal = null;
	    try {
		myLog.log(1, "Fetching proposal...");
		proposal = OSS_Utilities.fetchProposal(proposalPath);
	    } catch (IOException iox) {
		myLog.log(1, CLASS, id, "exec",
			   "Proposal: "+proposalPath.toString()+
			   " Error fetching from Database: "+iox);
		return error(TRANSACTION.DATABASE_READ_ERROR,			   
				     "Proposal: "+proposalPath.toString()+
				     " Error reading from database: "+iox);
	    }		
	    myLog.log(1, "Fetched Proposal: "+proposalPath);

	    // Check if exists.
	    if (proposal == null) {
		myLog.log(1, CLASS, id, "exec", 
			   "Proposal: "+proposalPath.toString()+" Not in database.");		
		return error(TRANSACTION.PATH_RESOLUTION_ERROR, 
				     "Could not find Proposal: "
				     +proposalPath.getProposalByName());
	    } 
		
	    // Make sure the key will unlock the Proposal.
	    if (!proposal.isLocked()) {
		 // The Proposal isn't locked anyway.
		    myLog.log(1, CLASS, id, "exec",
			       "Proposal ["+proposal.getFullPath()+
			       " Attempt to update Proposal which was not locked:");		    
		    return error(TRANSACTION.DATABASE_OBJECT_NOT_LOCKED,
					 "Attempt to update Proposal which was not locked: "
					 +proposal.getName());
	    }
	    
	    
	    // Check we can unLock it.
	    if (!proposal.unLock(key)) {
		// We have the wrong key.
		myLog.log(1, CLASS, id, "exec",
			   "Proposal ["+proposal.getFullPath()+
			   " Invalid key for unLock:");
		
		return error(TRANSACTION.ILLEGAL_DATABASE_ACCESS,
				     "You do not have a valid key for this Proposal: "
				     +proposal.getName());
	    }
		
	    // OK SO far - Now unhook any CoIs from the Proposal and record them.
	    Vector coilist = new Vector();
	    User coi  = null;			
	    Tag  tag  = null;
	    Root root = OSS_Utilities.fetchRoot();
	   
	    myLog.log(1, CLASS, id, "exec", "Detaching CoIs from old Proposal...");
	    if (root != null) {
		Iterator it1 = root.listAllTags();
		while (it1.hasNext()) {
		    tag = (Tag)it1.next();
		    Iterator it2 = tag.listAllUsers();
		    while (it2.hasNext()) {		
			coi = (User)it2.next();
			if (coi.findCoIProposal(proposal.getFullPath()) != null) {
			    coi.deleteCoIProposal(proposal.getFullPath());
			    coilist.addElement(coi); 
			    
			    myLog.log(1, CLASS, id, "exec", "Detached CoI: "+coi.getFullPath());
			}
		    }
		}
	    }

	    // Now we check to see if there were any FixedGroups which have been deleted.
	    myLog.log(1, CLASS, id, "exec", "Checking for any old fixed groups to detach...");
	    Iterator ig = proposal.listAllGroups();
	    while (ig.hasNext()) {
		Group group = (Group)ig.next();
		if (group instanceof FixedGroup) {
		    FixedGroup fg = (FixedGroup)group;
		    Scheduling.removeFixedGroup(fg);
		    myLog.log(1, CLASS, id, "exec", "Removed fixed group: "+fg.getName()+
				     " at "+sdf.format(new Date(fg.getFixedTime())));
		}
	    }
	    
	    // Delete the Proposal from the PI's list.
	    user.deleteProposal(proposal.getName());
	    myLog.log(1, CLASS, id, "exec", "Removed old version from User's Proposal list:"+user.getName());
	    
	    // Slot the modified Proposal in.
	    user.addProposal(replacementProposal.getName());
	    myLog.log(1, CLASS, id, "exec", "Added replacement version to User's Proposal list");
	    
	    // Dont forget to unlock it!;
	    replacementProposal.unLock(key);
	    replacementProposal.unLockChildren(key);
	    replacementProposal.setLastUnLocked(System.currentTimeMillis());
	    myLog.log(1, CLASS, id, "exec", "Unlocked replacement version: with full unlock");
	    
	    myLog.log(1, CLASS, id, "exec", "Checking lock is set to: "+replacementProposal.getLock()+"...");
	    // The path should NOT need updating here as it should have been set
	    // already. Alternative would be to use updateChildren() in the addXX().
	    myLog.log(1, CLASS, id, "exec", "Checking I can unlock it...");
	    if (replacementProposal.canLock())		
		myLog.log(1, CLASS, id, "exec",  "Can I lock it ? - YES");
	    else 
		myLog.log(1, CLASS, id, "exec",  "Can I lock it ?- APPARENTLY NOT");

	    // Revision info.
	    //replacementProposal.addRevisionInfo(System.currentTimeMillis(), 
	    //				"OSS",
	    //				0xCCAABBEEL,
	    //				"TMC Update");
	    
	    
	    // Now re-attach the recorded CoIs to the new Proposal.	

	    myLog.log(1, CLASS, id, "exec", "Re-attaching CoIs...");	
	    if (coilist != null) {
		Iterator it = coilist.iterator();
		while (it.hasNext()) {
		    coi = (User)it.next();
		    coi.addCoIProposal(replacementProposal.getFullPath());
		    myLog.log(1, CLASS, id, "exec", "Attached CoI: "+coi.getFullPath());
		}
	    }

	    myLog.log(1, CLASS, id, "exec", "Attaching new  fixed groups...");
	    Iterator ign = replacementProposal.listAllGroups();
	    while (ign.hasNext()) {
		Group group = (Group)ign.next();
		if (group instanceof FixedGroup) {
		    FixedGroup fg = (FixedGroup)group;
		    Scheduling.modifyFixedGroup(fg);
		    myLog.log(1, CLASS, id, "exec", "Adding fixed group: "+fg.getName()+
				     " at "+sdf.format(new Date(fg.getFixedTime())));
		}
	    }

	    myLog.log(1, CLASS, id, "exec", "Updating Group Execution times...");
	    // Now Update the execution times for all Groups.
	    Group    group = null;
	    Iterator it    = replacementProposal.listAllGroups();
	    while (it.hasNext()) {
		group = (Group)it.next();
		Scheduling.calculateExecutionTimes(group);
		myLog.log(1, CLASS, id, "exec", "Group: "+group.getName()+
			  ", Maximal: "+(group.getMaximalExecutionTime()/1000.0)+" secs."+
			  ", Nominal: "+(group.getNominalExecutionTime()/1000.0)+" secs."+
			  ", Slewing: "+(group.getSlewTime()/1000.0)+" secs.");
		
		Scheduling.updateMetricsCache(group);
		myLog.log(1, CLASS, id, "exec", "Group: "+group.getName()+
			  "Maximal elevation in night: "+Position.toDegrees(Scheduling.getMetricsCacheValue(group),2));
	    }
	    
	    // Store it
	    try {
		myLog.log(1, CLASS, id, "exec", "Stroing updated proposal details...");
		OSS_Utilities.storeProposal(replacementProposal);
	    } catch (IOException ix) {
		logger.log(1, CLASS, id, "exec",
			   "Proposal ["+proposal.getFullPath()+
			   "] Unable to Store: "+ix);
		return error(TRANSACTION.DATABASE_UPDATE_ERROR, 
				     "Proposal ["+proposal.getFullPath()+
				     "] Unable to Store: "+ix);
	    }
	    
	    myLog.log(1, CLASS, id, "exec", "Saved all changes to database");
	    	    
	} finally {
	    t.commit();
	}
	
	REPLACE_PROPOSAL_DONE done = new REPLACE_PROPOSAL_DONE(id);
	done.setSuccessful(true);
	return done;

    }
    
}

/** $Log: REPLACE_PROPOSALImpl.java,v $
/** Revision 1.1  2006/11/27 08:45:00  snf
/** Initial revision
/**
/** Revision 1.2  2001/04/27 17:22:00  snf
/** Adda proposal.
/**
/** Revision 1.1  2001/02/23 18:51:14  snf
/** Initial revision
/** */
